package main

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
)

// Docker Registry 配置
const (
	REGISTRY_HOST = "192.168.3.66:5000"
	REGISTRY_URL  = "http://" + REGISTRY_HOST
)

// 删除模式配置
var (
	ENABLE_PHYSICAL_DELETE = false // 是否启用物理删除
)

// 删除状态存储文件
const (
	DELETED_IMAGES_FILE = "deleted_images.json"
	DELETED_TAGS_FILE   = "deleted_tags.json"
)

// 已删除的镜像列表（模拟删除）
var deletedImages = make(map[string]bool)

// 已删除的镜像标签列表（模拟按标签删除）
var deletedImageTags = make(map[string]map[string]bool)

// 删除状态数据结构
type DeletedState struct {
	Images map[string]bool            `json:"images"`
	Tags   map[string]map[string]bool `json:"tags"`
}

// Registry API 响应结构
type RegistryResponse struct {
	Repositories []string `json:"repositories"`
}

type TagsResponse struct {
	Name string   `json:"name"`
	Tags []string `json:"tags"`
}

type ManifestResponse struct {
	SchemaVersion int    `json:"schemaVersion"`
	MediaType     string `json:"mediaType"`
	Config        struct {
		Size   int    `json:"size"`
		Digest string `json:"digest"`
	} `json:"config"`
	Layers []struct {
		Size   int    `json:"size"`
		Digest string `json:"digest"`
	} `json:"layers"`
}

// 镜像配置信息结构
type ImageConfig struct {
	Created time.Time `json:"created"`
	Config  struct {
		Labels map[string]string `json:"labels"`
	} `json:"config"`
}

// 镜像信息结构
type ImageInfo struct {
	Name        string    `json:"name"`
	Tags        []string  `json:"tags"`
	Size        int64     `json:"size"`
	CreatedAt   time.Time `json:"createdAt"`
	UpdatedAt   time.Time `json:"updatedAt"`
	Description string    `json:"description"`
	PullCount   int       `json:"pullCount"`
}

type ImageDetail struct {
	Name        string    `json:"name"`
	Tag         string    `json:"tag"`
	Size        int64     `json:"size"`
	Digest      string    `json:"digest"`
	CreatedAt   time.Time `json:"createdAt"`
	Layers      []Layer   `json:"layers"`
	Config      Config    `json:"config"`
	PullCommand string    `json:"pullCommand"`
}

type Layer struct {
	Digest string `json:"digest"`
	Size   int64  `json:"size"`
}

type Config struct {
	Digest string `json:"digest"`
	Size   int64  `json:"size"`
}

// Registry HTTP 客户端
func makeRegistryRequest(endpoint string) ([]byte, error) {
	url := REGISTRY_URL + endpoint

	client := &http.Client{
		Timeout: 30 * time.Second,
	}

	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %v", err)
	}

	// 为manifest请求添加正确的Accept头
	if strings.Contains(endpoint, "/manifests/") {
		req.Header.Set("Accept", "application/vnd.docker.distribution.manifest.v2+json")
	}

	resp, err := client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("failed to connect to registry: %v", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("registry returned status %d", resp.StatusCode)
	}

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("failed to read response: %v", err)
	}

	return body, nil
}

// 加载删除状态
func loadDeletedState() {
	// 加载删除的镜像
	if data, err := os.ReadFile(DELETED_IMAGES_FILE); err == nil {
		json.Unmarshal(data, &deletedImages)
	}

	// 加载删除的标签
	if data, err := os.ReadFile(DELETED_TAGS_FILE); err == nil {
		json.Unmarshal(data, &deletedImageTags)
	}

	fmt.Printf("Loaded deleted state: %d images, %d image groups with deleted tags\n",
		len(deletedImages), len(deletedImageTags))
}

// 保存删除状态
func saveDeletedState() {
	// 保存删除的镜像
	if data, err := json.Marshal(deletedImages); err == nil {
		os.WriteFile(DELETED_IMAGES_FILE, data, 0644)
	}

	// 清理空的标签map
	cleanedTags := make(map[string]map[string]bool)
	for imageName, tags := range deletedImageTags {
		if len(tags) > 0 {
			cleanedTags[imageName] = tags
		}
	}

	// 保存删除的标签
	if data, err := json.Marshal(cleanedTags); err == nil {
		os.WriteFile(DELETED_TAGS_FILE, data, 0644)
	}
}

// 获取镜像的真实创建时间
func getImageCreatedTime(repo, tag string) time.Time {
	// 获取manifest
	manifestBody, err := makeRegistryRequest("/v2/" + repo + "/manifests/" + tag)
	if err != nil {
		return time.Now().AddDate(0, 0, -7) // 默认7天前
	}

	var manifest ManifestResponse
	if err := json.Unmarshal(manifestBody, &manifest); err != nil {
		return time.Now().AddDate(0, 0, -7)
	}

	// 获取config blob
	configBody, err := makeRegistryRequest("/v2/" + repo + "/blobs/" + manifest.Config.Digest)
	if err != nil {
		return time.Now().AddDate(0, 0, -7)
	}

	var config ImageConfig
	if err := json.Unmarshal(configBody, &config); err != nil {
		return time.Now().AddDate(0, 0, -7)
	}

	if !config.Created.IsZero() {
		return config.Created
	}

	return time.Now().AddDate(0, 0, -7)
}

// 获取镜像列表
func getImageList(c *gin.Context) {
	// 获取查询参数
	search := c.Query("search")
	page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
	limit, _ := strconv.Atoi(c.DefaultQuery("limit", "20"))

	// 从 Registry 获取仓库列表
	body, err := makeRegistryRequest("/v2/_catalog")
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"error": "Failed to connect to registry: " + err.Error(),
		})
		return
	}

	var registryResp RegistryResponse
	if err := json.Unmarshal(body, &registryResp); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"error": "Failed to parse registry response",
		})
		return
	}

	// 获取每个仓库的详细信息
	var images []ImageInfo
	for _, repo := range registryResp.Repositories {
		// 检查镜像是否已被删除（模拟删除）
		if deletedImages[repo] {
			continue
		}

		// 如果有搜索条件，过滤仓库名
		if search != "" && !strings.Contains(strings.ToLower(repo), strings.ToLower(search)) {
			continue
		}

		// 获取标签列表
		tagsBody, err := makeRegistryRequest("/v2/" + repo + "/tags/list")
		if err != nil {
			continue // 跳过无法获取标签的仓库
		}

		var tagsResp TagsResponse
		if err := json.Unmarshal(tagsBody, &tagsResp); err != nil {
			continue
		}

		// 过滤掉已删除的标签
		var availableTags []string
		for _, tag := range tagsResp.Tags {
			if deletedImageTags[repo] == nil || !deletedImageTags[repo][tag] {
				availableTags = append(availableTags, tag)
			}
		}

		// 如果所有标签都被删除了，跳过这个镜像
		if len(availableTags) == 0 {
			continue
		}

		// 获取第一个可用标签的详细信息来计算大小和时间
		var totalSize int64 = 0
		var createdAt time.Time = time.Now().AddDate(0, 0, -7) // 默认7天前
		var updatedAt time.Time = time.Now().AddDate(0, 0, -1) // 默认1天前

		if len(availableTags) > 0 {
			// 使用第一个可用标签获取manifest信息
			tag := availableTags[0]
			manifestBody, err := makeRegistryRequest("/v2/" + repo + "/manifests/" + tag)
			if err == nil {
				var manifest ManifestResponse
				if err := json.Unmarshal(manifestBody, &manifest); err == nil {
					// 计算总大小
					totalSize = int64(manifest.Config.Size)
					for _, layer := range manifest.Layers {
						totalSize += int64(layer.Size)
					}
				}
			}

			// 获取真实的创建时间
			createdAt = getImageCreatedTime(repo, tag)
			// 更新时间设为创建时间之后
			updatedAt = createdAt.AddDate(0, 0, 1)
		}

		// 创建镜像信息
		image := ImageInfo{
			Name:        repo,
			Tags:        availableTags,
			Size:        totalSize,
			CreatedAt:   createdAt,
			UpdatedAt:   updatedAt,
			Description: fmt.Sprintf("Docker image: %s", repo),
			PullCount:   0, // Registry API 不提供此信息
		}

		images = append(images, image)
	}

	// 排序
	sort.Slice(images, func(i, j int) bool {
		return images[i].UpdatedAt.After(images[j].UpdatedAt)
	})

	// 分页
	total := len(images)
	start := (page - 1) * limit
	end := start + limit

	if start >= total {
		images = []ImageInfo{}
	} else if end > total {
		images = images[start:]
	} else {
		images = images[start:end]
	}

	c.JSON(http.StatusOK, gin.H{
		"data": images,
		"pagination": gin.H{
			"page":  page,
			"limit": limit,
			"total": total,
			"pages": (total + limit - 1) / limit,
		},
	})
}

// 获取镜像详情
func getImageDetail(c *gin.Context) {
	name := strings.TrimPrefix(c.Param("name"), "/")
	tag := c.DefaultQuery("tag", "latest")

	// 获取 manifest
	manifestBody, err := makeRegistryRequest("/v2/" + name + "/manifests/" + tag)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{
			"error": "Image not found: " + err.Error(),
		})
		return
	}

	var manifest ManifestResponse
	if err := json.Unmarshal(manifestBody, &manifest); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"error": "Failed to parse manifest",
		})
		return
	}

	// 计算总大小
	var totalSize int64 = int64(manifest.Config.Size)
	var layers []Layer

	for _, layer := range manifest.Layers {
		totalSize += int64(layer.Size)
		layers = append(layers, Layer{
			Digest: layer.Digest,
			Size:   int64(layer.Size),
		})
	}

	detail := ImageDetail{
		Name:      name,
		Tag:       tag,
		Size:      totalSize,
		Digest:    manifest.Config.Digest,
		CreatedAt: time.Now(), // 模拟数据
		Layers:    layers,
		Config: Config{
			Digest: manifest.Config.Digest,
			Size:   int64(manifest.Config.Size),
		},
		PullCommand: fmt.Sprintf("docker pull %s/%s:%s", REGISTRY_HOST, name, tag),
	}

	c.JSON(http.StatusOK, gin.H{
		"data": detail,
	})
}

// 删除镜像
func deleteImage(c *gin.Context) {
	name := strings.TrimPrefix(c.Param("name"), "/")
	tag := c.DefaultQuery("tag", "latest")

	// 步骤1: 获取镜像的manifest以获取digest
	manifestURL := fmt.Sprintf("/v2/%s/manifests/%s", name, tag)

	client := &http.Client{Timeout: 30 * time.Second}
	req, err := http.NewRequest("GET", REGISTRY_URL+manifestURL, nil)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"success": false,
			"error":   "Failed to create manifest request",
		})
		return
	}

	// 设置正确的Accept头来获取manifest v2
	req.Header.Set("Accept", "application/vnd.docker.distribution.manifest.v2+json")

	resp, err := client.Do(req)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"success": false,
			"error":   "Failed to get manifest",
		})
		return
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		c.JSON(http.StatusNotFound, gin.H{
			"success": false,
			"error":   "Image not found",
		})
		return
	}

	// 获取manifest的digest从响应头
	manifestDigest := resp.Header.Get("Docker-Content-Digest")
	if manifestDigest == "" {
		c.JSON(http.StatusInternalServerError, gin.H{
			"success": false,
			"error":   "Failed to get manifest digest",
		})
		return
	}

	// 步骤2: 使用manifest digest删除镜像 (物理删除)
	// 注意：这需要Registry支持删除操作，通常需要在Registry配置中启用
	deleteURL := fmt.Sprintf("/v2/%s/manifests/%s", name, manifestDigest)

	// 创建DELETE请求
	deleteReq, err := http.NewRequest("DELETE", REGISTRY_URL+deleteURL, nil)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"success": false,
			"error":   "Failed to create delete request",
		})
		return
	}

	deleteResp, err := client.Do(deleteReq)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"success": false,
			"error":   "Failed to delete image: " + err.Error(),
		})
		return
	}
	defer deleteResp.Body.Close()

	if deleteResp.StatusCode == http.StatusAccepted || deleteResp.StatusCode == http.StatusNoContent {
		c.JSON(http.StatusOK, gin.H{
			"success": true,
			"message": fmt.Sprintf("Image %s:%s physically deleted from registry", name, tag),
			"note":    "Physical deletion completed. Run 'registry garbage-collect' to free storage space.",
			"digest":  manifestDigest,
		})
	} else if deleteResp.StatusCode == http.StatusMethodNotAllowed {
		// Registry不支持物理删除，回退到模拟删除
		if deletedImageTags[name] == nil {
			deletedImageTags[name] = make(map[string]bool)
		}
		deletedImageTags[name][tag] = true

		// 保存删除状态到文件
		saveDeletedState()

		c.JSON(http.StatusOK, gin.H{
			"success": true,
			"message": fmt.Sprintf("Image tag %s:%s marked for deletion (simulated)", name, tag),
			"note":    "Registry physical deletion is disabled. Tag removed from list but still exists in registry.",
			"help":    "To enable physical deletion: set REGISTRY_STORAGE_DELETE_ENABLED=true in registry config",
		})
	} else if deleteResp.StatusCode == http.StatusNotFound {
		c.JSON(http.StatusNotFound, gin.H{
			"success": false,
			"error":   "Image or tag not found in registry",
		})
	} else {
		// 读取错误响应体获取详细信息
		body, _ := io.ReadAll(deleteResp.Body)
		c.JSON(http.StatusInternalServerError, gin.H{
			"success": false,
			"error":   fmt.Sprintf("Failed to delete image, registry returned status %d", deleteResp.StatusCode),
			"details": string(body),
		})
	}
}

// 恢复已删除的镜像
func restoreImage(c *gin.Context) {
	name := strings.TrimPrefix(c.Param("name"), "/")
	tag := c.DefaultQuery("tag", "")

	if tag != "" {
		// 恢复特定标签
		if deletedImageTags[name] != nil && deletedImageTags[name][tag] {
			delete(deletedImageTags[name], tag)
			// 如果该镜像没有其他已删除的标签，清理map
			if len(deletedImageTags[name]) == 0 {
				delete(deletedImageTags, name)
			}

			// 保存删除状态到文件
			saveDeletedState()

			c.JSON(http.StatusOK, gin.H{
				"success": true,
				"message": fmt.Sprintf("Image tag %s:%s restored successfully", name, tag),
			})
		} else {
			c.JSON(http.StatusNotFound, gin.H{
				"success": false,
				"error":   "Image tag was not deleted or does not exist",
			})
		}
	} else {
		// 恢复整个镜像（所有标签）
		if deletedImages[name] {
			delete(deletedImages, name)

			// 保存删除状态到文件
			saveDeletedState()

			c.JSON(http.StatusOK, gin.H{
				"success": true,
				"message": fmt.Sprintf("Image %s restored successfully", name),
			})
		} else if deletedImageTags[name] != nil {
			// 恢复所有已删除的标签
			delete(deletedImageTags, name)

			// 保存删除状态到文件
			saveDeletedState()

			c.JSON(http.StatusOK, gin.H{
				"success": true,
				"message": fmt.Sprintf("All tags for image %s restored successfully", name),
			})
		} else {
			c.JSON(http.StatusNotFound, gin.H{
				"success": false,
				"error":   "Image was not deleted or does not exist",
			})
		}
	}
}

// 获取镜像标签列表
func getImageTags(c *gin.Context) {
	name := strings.TrimPrefix(c.Param("name"), "/")

	body, err := makeRegistryRequest("/v2/" + name + "/tags/list")
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{
			"error": "Image not found: " + err.Error(),
		})
		return
	}

	var tagsResp TagsResponse
	if err := json.Unmarshal(body, &tagsResp); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"error": "Failed to parse tags response",
		})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"data": tagsResp,
	})
}

// 检查 Registry 连接状态
func checkRegistryHealth(c *gin.Context) {
	_, err := makeRegistryRequest("/v2/")
	if err != nil {
		c.JSON(http.StatusServiceUnavailable, gin.H{
			"status":   "error",
			"message":  "Registry unavailable: " + err.Error(),
			"registry": REGISTRY_HOST,
		})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"status":   "ok",
		"message":  "Registry is healthy",
		"registry": REGISTRY_HOST,
	})
}

// 获取删除配置
func getDeleteConfig(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"physical_delete_enabled": ENABLE_PHYSICAL_DELETE,
		"registry_url":            REGISTRY_URL,
		"note":                    "Physical deletion requires REGISTRY_STORAGE_DELETE_ENABLED=true in registry config",
	})
}

// 设置删除配置
func setDeleteConfig(c *gin.Context) {
	var config struct {
		EnablePhysicalDelete bool `json:"enable_physical_delete"`
	}

	if err := c.ShouldBindJSON(&config); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"success": false,
			"error":   "Invalid request body",
		})
		return
	}

	ENABLE_PHYSICAL_DELETE = config.EnablePhysicalDelete

	c.JSON(http.StatusOK, gin.H{
		"success":                 true,
		"physical_delete_enabled": ENABLE_PHYSICAL_DELETE,
		"message":                 fmt.Sprintf("Physical deletion %s", map[bool]string{true: "enabled", false: "disabled"}[ENABLE_PHYSICAL_DELETE]),
	})
}

// 检查Docker是否可用
func checkDockerAvailable() error {
	cmd := exec.Command("docker", "--version")
	return cmd.Run()
}

// 上传镜像
func uploadImage(c *gin.Context) {
	// 首先检查Docker是否可用
	if err := checkDockerAvailable(); err != nil {
		c.JSON(http.StatusServiceUnavailable, gin.H{
			"success": false,
			"error":   "Docker is not available on this server",
			"message": "Please ensure Docker is installed and running on the server",
			"help":    "Contact administrator to install Docker or use alternative upload methods",
		})
		return
	}

	// 获取上传的文件
	file, header, err := c.Request.FormFile("image")
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"success": false,
			"error":   "No file uploaded or invalid file",
		})
		return
	}
	defer file.Close()

	// 检查文件类型
	filename := header.Filename
	if !strings.HasSuffix(filename, ".tar") && !strings.HasSuffix(filename, ".tar.gz") {
		c.JSON(http.StatusBadRequest, gin.H{
			"success": false,
			"error":   "Only .tar and .tar.gz files are supported",
			"help":    "Please use 'docker save -o image.tar image:tag' to create a valid image file",
		})
		return
	}

	// 检查文件大小（限制为1GB）
	const maxFileSize = 1024 * 1024 * 1024 // 1GB
	if header.Size > maxFileSize {
		c.JSON(http.StatusBadRequest, gin.H{
			"success": false,
			"error":   "File size too large",
			"message": fmt.Sprintf("Maximum file size is %d MB", maxFileSize/(1024*1024)),
		})
		return
	}

	// 创建临时目录
	tempDir, err := os.MkdirTemp("", "docker-image-upload-")
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"success": false,
			"error":   "Failed to create temporary directory",
		})
		return
	}
	defer os.RemoveAll(tempDir)

	// 保存上传的文件
	tempFile := filepath.Join(tempDir, filename)
	out, err := os.Create(tempFile)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"success": false,
			"error":   "Failed to save uploaded file",
		})
		return
	}

	_, err = io.Copy(out, file)
	out.Close()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"success": false,
			"error":   "Failed to save uploaded file",
		})
		return
	}

	// 使用docker load命令加载镜像
	cmd := exec.Command("docker", "load", "-i", tempFile)
	output, err := cmd.CombinedOutput()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"success": false,
			"error":   "Failed to load docker image",
			"details": string(output),
			"help":    "Please ensure the uploaded file is a valid Docker image created with 'docker save'",
		})
		return
	}

	// 解析docker load的输出获取镜像名称
	outputStr := string(output)
	var imageName string
	lines := strings.Split(outputStr, "\n")
	for _, line := range lines {
		if strings.Contains(line, "Loaded image:") {
			parts := strings.Split(line, "Loaded image:")
			if len(parts) > 1 {
				imageName = strings.TrimSpace(parts[1])
				break
			}
		}
	}

	if imageName == "" {
		c.JSON(http.StatusInternalServerError, gin.H{
			"success": false,
			"error":   "Failed to determine loaded image name",
			"output":  outputStr,
			"help":    "The uploaded file may not be a valid Docker image",
		})
		return
	}

	// 推送镜像到Registry
	registryImageName := fmt.Sprintf("%s/%s", REGISTRY_HOST, imageName)

	// 重新标记镜像
	tagCmd := exec.Command("docker", "tag", imageName, registryImageName)
	if err := tagCmd.Run(); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"success": false,
			"error":   "Failed to tag image for registry",
			"image":   imageName,
			"target":  registryImageName,
		})
		return
	}

	// 推送到Registry
	pushCmd := exec.Command("docker", "push", registryImageName)
	pushOutput, err := pushCmd.CombinedOutput()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"success": false,
			"error":   "Failed to push image to registry",
			"details": string(pushOutput),
			"help":    "Please check registry connectivity and permissions",
		})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"success":       true,
		"message":       "Image uploaded and pushed to registry successfully",
		"image_name":    imageName,
		"registry_name": registryImageName,
		"output":        outputStr,
		"file_size":     header.Size,
	})
}

// 检查上传环境状态
func checkUploadEnvironment(c *gin.Context) {
	status := gin.H{
		"docker_available":  false,
		"registry_url":      REGISTRY_URL,
		"max_file_size":     1024 * 1024 * 1024, // 1GB
		"supported_formats": []string{".tar", ".tar.gz"},
	}

	// 检查Docker是否可用
	if err := checkDockerAvailable(); err == nil {
		status["docker_available"] = true
		status["message"] = "Upload environment is ready"
	} else {
		status["message"] = "Docker is not available on this server"
		status["error"] = "Please contact administrator to install Docker"
	}

	c.JSON(http.StatusOK, status)
}
